package com.cgs.query.handler.statement

import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.domain.ParameterMapping
import com.cgs.query.domain.ParameterMode
import com.cgs.query.executor.ExecutionContext
import java.sql.*


/**简单jdbc*/
class SimpleStatementHandler(
        config: QueryConfiguration,
        executionContext: ExecutionContext
) : BaseStatementHandler(config, executionContext) {

    override fun instantiateStatement(connection: Connection): Statement {
        return connection.createStatement()
    }

    override fun parameterize(statement: Statement) {
    }

    override fun update(statement: Statement): Int {
        statement.executeQuery(this.boundSql.sql)
        return statement.updateCount
    }

    override fun query(statement: Statement): List<Any> {
        statement.execute(this.boundSql.sql)
        return this.config.resultSetHandler.handleResultSet(true, statement, mappedStatement.limit)
    }

    override fun execute(statement: Statement): List<Any> {
        return this.config.resultSetHandler.handleResultSet(statement.execute(this.boundSql.sql), statement, mappedStatement.limit)
    }
}

/**预编译jdbc*/
class PreparedStatementHandler(
        config: QueryConfiguration,
        executionContext: ExecutionContext
) : BaseStatementHandler(config, executionContext) {

    override fun instantiateStatement(connection: Connection): Statement {
        return connection.prepareStatement(this.boundSql.sql)
    }

    override fun parameterize(statement: Statement) {
        val ps = statement as PreparedStatement
        this.boundSql.parameterMappings.forEachIndexed { i, v ->
            ps.setObject(i + 1, v.value)
        }
    }

    override fun update(statement: Statement): Int {
        val ps = statement as PreparedStatement
        ps.execute()
        return ps.updateCount
    }

    override fun query(statement: Statement): List<Any> {
        val ps = statement as PreparedStatement
        ps.execute()
        return this.config.resultSetHandler.handleResultSet(true, ps, mappedStatement.limit)
    }

    override fun execute(statement: Statement): List<Any> {
        val ps = statement as PreparedStatement
        return this.config.resultSetHandler.handleResultSet(ps.execute(), ps, mappedStatement.limit)
    }
}

/**存储过程jdbc*/
class CallableStatementHandler(
        config: QueryConfiguration,
        executionContext: ExecutionContext
) : BaseStatementHandler(config, executionContext) {

    private val outParams = hashMapOf<Int, ParameterMapping>()

    override fun instantiateStatement(connection: Connection): Statement {
        return connection.prepareCall(this.boundSql.sql)
    }

    override fun parameterize(statement: Statement) {
        val cs = statement as CallableStatement
        registerParameters({ it, mp ->
            cs.registerOutParameter(it, mp.jdbcType.vendorTypeNumber)
            outParams[it] = mp
        }, { it, mp ->
            cs.setObject(it, mp.value)
        })
    }

    override fun update(statement: Statement): Int {
        val ps = statement as CallableStatement
        ps.execute()
        return ps.updateCount
    }

    override fun query(statement: Statement): List<Any> {
        val cs = statement as CallableStatement
        cs.execute()
        return this.config.resultSetHandler.handleOutputParameters(true, cs, outParams, mappedStatement.limit)
    }

    override fun execute(statement: Statement): List<Any> {
        val cs = statement as CallableStatement
        return this.config.resultSetHandler.handleOutputParameters(cs.execute(), cs, outParams, mappedStatement.limit)
    }

    private fun registerParameters(registerOutParameter: (it: Int, value: ParameterMapping) -> Unit, registerInParameter: (it: Int, value: ParameterMapping) -> Unit) {
        val parameterMappings = boundSql.parameterMappings
        (1..parameterMappings.size).forEach {
            val parameterMapping = parameterMappings[it - 1]
            when (parameterMapping.mode) {
                ParameterMode.OUT -> registerOutParameter(it, parameterMapping)
                ParameterMode.IN -> registerInParameter(it, parameterMapping)
                else -> {
                    registerOutParameter(it, parameterMapping)
                    registerInParameter(it, parameterMapping)
                }
            }
        }
    }
}

